﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Cofe.Core.Property;
using System.Reflection;

namespace Cofe.Core.Dynamic
{
    [Export(typeof(ICofeService))]
    [ServicePriority(ServicePriorityAttribute.DefaultPriority_COFE)]
    public class CofeDynamicInterfaceManager : IDynamicPropertyInterfaceManager
    {

        #region Constructor

        #endregion

        #region Methods

        public bool CanCastAs<T>(IPropertyHost entry) where T : IDynamicInterface<IPropertyHost>
        {
            foreach (var p in AllInterfaceProviders)
            {
                if (p.CanCastAs<T>(entry))
                    return true;
            }
            return false;
        }

        public T CastAs<T>(IPropertyHost entry) where T : IDynamicInterface<IPropertyHost>
        {
            foreach (var p in AllInterfaceProviders)
            {
                if (p.CanCastAs<T>(entry))
                    return p.CastAs<T>(entry);
            }

            CofeServices.Logger.Log(String.Format("CDPM: Cannot cast {0} to {1}",
                entry.TryGetProperty<string>(CofeProperties.FullParseName, "Unknown"), typeof(T).FullName), Category.Warn, Priority.Medium);
            return default(T);
        }

        internal IEnumerable<Type> GetCastableContainers()
        {
            return AllInterfaceProviders.SelectMany(p => p.CastableContainers).Distinct();
        }

        internal IEnumerable<Type> GetCastableInterfaces(bool includeInheritedInterfaces)
        {
            var allInterfaces = AllInterfaceProviders.SelectMany(p => p.CastableInterfaces).Distinct();

            List<Type> itemToRemove = new List<Type>();
            if (!includeInheritedInterfaces)
            {

                foreach (var i1 in allInterfaces)
                    foreach (var i2 in allInterfaces)
                        if ((!i2.Equals(i1) && i1.IsAssignableFrom(i2)) || i1.GetTypeInfo().IsClass)
                        {
                            itemToRemove.Add(i1);
                            break;
                        }
            }

            foreach (var i in allInterfaces)
            {
                if (!itemToRemove.Contains(i))
                    yield return i;
            }
        }

        public IEnumerable<Type> GetCastableInterfaces(IPropertyHost propertyHost, bool includeInheritedInterfaces = false)
        {
            foreach (var castableType in (includeInheritedInterfaces ? AllCastableInterfaces : CastableInterfaces))
                foreach (var provider in AllInterfaceProviders)
                {
                    if (provider.CanCastAs(propertyHost, castableType))
                    {
                        yield return castableType;
                        break;
                    }
                }
        }

        #endregion

        #region Data

        private IDynamicPropertyInterfaceProvider[] _allInterfaceProviders = null;
        private Type[] _allCastableInterfaces = null;
        private Type[] _allCastableContainers = null;
        private Type[] _castableInterfaces = null;

        #endregion

        #region Public Properties

        public IDynamicPropertyInterfaceProvider[] AllInterfaceProviders
        {
            get
            {
                return _allInterfaceProviders ?? (_allInterfaceProviders =
                    CofeServices.ServiceLocater.FindAllServices<IDynamicPropertyInterfaceProvider>().ToArray());
            }
        }

        public Type[] AllCastableInterfaces
        {
            get { return _allCastableInterfaces ?? (_allCastableInterfaces = GetCastableInterfaces(true).ToArray()); }
        }


        public Type[] AllCastableContainers
        {
            get { return _allCastableContainers ?? (_allCastableContainers = GetCastableContainers().ToArray()); }
        }

        public Type[] CastableInterfaces
        {
            get { return _castableInterfaces ?? (_castableInterfaces = GetCastableInterfaces(false).ToArray()); }
        }

        #endregion
    }
}
